package org.ourgrid.broker.controlws.gatewayws.transferserver;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import org.ourgrid.broker.controlws.gatewayws.Broker3GConstants;
import org.ourgrid.broker.controlws.gatewayws.dao.Broker3GDAO;
import org.ourgrid.broker.controlws.gatewayws.dao.JobSpec3G;

import br.edu.ufcg.lsd.commune.container.ContainerContext;
import br.edu.ufcg.lsd.commune.container.logging.CommuneLogger;
import br.edu.ufcg.lsd.commune.container.logging.CommuneLoggerFactory;

public class Broker3GTransferServer implements Runnable {

	private static final int PACKET_SIZE = 1024;
	private static final int PUT = 0;
	private static final int GET = 1;
	
	private final CommuneLogger logger = CommuneLoggerFactory.getInstance().getLogger(Broker3GConstants.LOG_CATEGORY);
	private final Broker3GDAO dao;
	private final ContainerContext context;

	public Broker3GTransferServer(Broker3GDAO dao, ContainerContext context) {
		this.dao = dao;
		this.context = context;
	}

	public void run() {
		
		int port = Integer.parseInt(context.getProperty(
				Broker3GConstants.BROKER_3G_TRANSFERPORT_PROP));
		
		ServerSocket socket;
		try {
			socket = new ServerSocket(port);
		} catch (IOException e) {
			logger.error("Error on server socket creation", e);
			throw new RuntimeException(e);
		}
		
		while (true) {
			Socket accept = null;
			try {
				accept = socket.accept();
			} catch (IOException e) {
				logger.warnException(e);
				continue;
			}
			
			
			FileOutputStream fileOutputStream = null;
			
			try {
				
				InputStream inputStream = accept.getInputStream();
				String readChunk = read(inputStream);
				
				String[] splittedChunk = readChunk.split(":");

				if (splittedChunk.length != 3) {
					throw new IOException("Stream in wrong format");
				}
				
				int method = Integer.valueOf(splittedChunk[0]);
				long mark = Long.valueOf(splittedChunk[1]);
				String fileName = splittedChunk[2];
				
				JobSpec3G job = dao.getJob(mark);
				
				if (job == null) {
					throw new IllegalArgumentException("Stream mark does not correspond to a valid job");
				}
				
				String jobDir = context.getProperty(Broker3GConstants.BROKER_3G_TMPDIR_PROP) + 
					Broker3GConstants.JOBFOLDER + File.separator + mark + File.separator;
				
				new File(jobDir).mkdirs();
				
				String fullFilePath =  jobDir + File.separator + fileName;
				
				switch (method) {
				case PUT:
					writeFile(fullFilePath, inputStream);
					break;

				case GET:
					readFile(fullFilePath, accept.getOutputStream());
					break;
					
				default:
					throw new IOException("Stream in wrong format");
				}
			
			} catch (Exception e) {
				logger.warnException(e);
			} finally {
				try {
					if (fileOutputStream != null) {
						fileOutputStream.close();
					}
					accept.close();
				} catch (IOException e) {
					logger.warnException(e);
				}
				
			}
			
		}
		
	}

	private void readFile(String fullFilePath, OutputStream outputStream) throws IOException {
		
		FileInputStream reader = new FileInputStream(fullFilePath);
		
		byte[] buffer = new byte[PACKET_SIZE];
		int len = 0;
		
		while ((len = reader.read(buffer, 0, PACKET_SIZE - 1)) != -1) {
			buffer[len] = '\0';
			outputStream.write(buffer, 0, len + 1);
		}
		
		buffer = new byte[PACKET_SIZE];
		byte[] eofBytes = "#EOF#".getBytes();
		
		for (int i = 0; i < eofBytes.length; i++) {
			buffer[i] = eofBytes[i];
		}
		
		outputStream.write(buffer, 0, PACKET_SIZE);
		reader.close();
	}

	private void writeFile(String fullFilePath, InputStream inputStream) throws IOException {
		
		FileOutputStream fileOutputStream = new FileOutputStream(fullFilePath);
		
		byte[] buffer = new byte[PACKET_SIZE];
		
		while (inputStream.read(buffer) != -1) {
			
			int len = buffer.length;
			
			for (int i = 0; i < buffer.length; i++) {
				if (buffer[i] == '\0') {
					len = i;
					break;
				}
			}
			
			fileOutputStream.write(buffer, 0, len);
			buffer = new byte[PACKET_SIZE];
		}
		
		fileOutputStream.close();
	}

	private String read(InputStream stream) throws IOException {
		byte[] buffer = new byte[1024];
		if (stream.read(buffer) == -1 ) {
			throw new IOException("Could not read from stream");
		}
		
		String string = new String(buffer);
		
		return string;
	}
	
}
